package com.arsn.myf.generate.service.impl;


import com.arsn.myf.common.constant.Constants;
import com.arsn.myf.common.util.StringUtils;
import com.arsn.myf.common.util.spring.SpringUtils;
import com.arsn.myf.generate.config.GenerateConfig;
import com.arsn.myf.generate.entity.GenerateCodeTable;
import com.arsn.myf.generate.entity.TableColumnInfo;
import com.arsn.myf.generate.mapper.GenerateCodeMapper;
import com.arsn.myf.generate.service.GenerateCodeService;
import com.arsn.myf.generate.util.VelocityUtil;
import com.arsn.myf.generate.vo.GenerateCodeTableVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.codec.Charsets;
import org.apache.commons.io.IOUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.net.URLEncoder;
import java.util.Arrays;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

import static com.arsn.myf.generate.config.GenerateConfig.basePath;

/**
 * 代码生成实现层
 *
 * @author zhongrj
 */
@Service
public class GenerateCodeServiceImpl extends ServiceImpl<GenerateCodeMapper, GenerateCodeTable> implements GenerateCodeService {

    /**
     * 自定义查询代码生成分页数据
     *
     * @param page
     * @param generateCode
     * @return
     */
    @Override
    public IPage<GenerateCodeTableVO> selectPageGenerateCodeList(Page<GenerateCodeTableVO> page, GenerateCodeTableVO generateCode) {
        return page.setRecords(baseMapper.selectPageGenerateCodeList(page, generateCode));
    }

    /**
     * 代码生成
     *
     * @param generateCodeTable
     * @return
     */
    @Override
    public void codeGenerate(GenerateCodeTable generateCodeTable,HttpServletResponse response) {
        //通过表id获取表列信息
        GenerateCodeTable table = getById(generateCodeTable.getId());
        List<TableColumnInfo> columnInfo = baseMapper.getTableColumnInfo(generateCodeTable.getId());
        //初始化 velocity
        VelocityUtil.initVelocity();
        //设置 velocity 上下文
        VelocityContext velocityContext = VelocityUtil.setVelocityContext(table, columnInfo);
        //获取模板列表
        List<String> templateList = VelocityUtil.getTemplateList();
        // 如果为 dev ,则导出为zip
        if (SpringUtils.getActiveProfile().equals("dev")) {
            saveZip(velocityContext,templateList,table,response);
        }
        // 如果为 test ,则保存到本地目录
        if (SpringUtils.getActiveProfile().equals("test")) {
            saveLocalDir(velocityContext,templateList,table);
        }
    }

    /**
     * 保存到 zip
     * @param velocityContext
     * @param templateList
     * @param generateCodeTable
     */
    public void saveZip(VelocityContext velocityContext,
                        List<String> templateList,
                        GenerateCodeTable generateCodeTable,
                        HttpServletResponse response){
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zip = new ZipOutputStream(outputStream);

        //模板遍历渲染
        templateList.forEach(template -> {
            // 渲染模板
            StringWriter stringWriter = new StringWriter();
            Template tpl = Velocity.getTemplate(template, Constants.UTF8);
            tpl.merge(velocityContext, stringWriter);
            try {
                zip.putNextEntry(new ZipEntry(getFileName(generateCodeTable.getEntityName(), template)));
                IOUtils.write(stringWriter.toString(), zip, Constants.UTF8);
                IOUtils.closeQuietly(stringWriter);
                zip.flush();
                zip.closeEntry();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + generateCodeTable.getTableName(), e);
            }
        });
        //需要关闭，否则zip中的文件会损坏
        IOUtils.closeQuietly(zip);
        // zip 返回
        try {
            genCode(response,outputStream.toByteArray(),generateCodeTable.getEntityName());
        } catch (IOException ioException) {
            ioException.printStackTrace();
        }
    }

    /**
     * 保存本地路径
     * @param velocityContext
     * @param templateList
     * @param generateCodeTable
     */
    public void saveLocalDir(VelocityContext velocityContext,List<String> templateList,GenerateCodeTable generateCodeTable){
        //模板遍历渲染
        templateList.forEach(template -> {
            // 注意 . 不能直接替换 应写 \\.
            String fileDir = getFileDir(generateCodeTable.getPackageName(), template);
            String fileName = getFileName(generateCodeTable.getEntityName(), template);
            String filePath = fileDir + "\\\\" + fileName;
            File directory = new File(fileDir);
            if (!directory.exists()) {
                directory.mkdirs();
            }
            FileWriter writer;
            try {
                writer = new FileWriter(filePath);
                Template tp = Velocity.getTemplate(template, Constants.UTF8);
                tp.merge(velocityContext, writer);
                writer.flush();
                writer.close();
            } catch (IOException e) {
                log.error("渲染模板失败，表名：" + generateCodeTable.getTableName(), e);
            }
        });
    }

    /**
     * 获取文件名称
     *
     * @param entityName
     * @param template
     * @return
     */
    private String getFileName(String entityName, String template) {
        String fileName = null;
        List<String> list = Arrays.asList(template.split("/"));
        String temp = list.get(list.size() - 1).split("\\.")[0];
        String suffix = "."+ template.split("\\.")[1];
        if (temp.equals("controller")) {
            fileName = entityName + "Controller" + suffix;
        }
        if (temp.equals("entity") || temp.equals("api")) {
            fileName = entityName + suffix;
        }
        if (temp.equals("service")) {
            fileName = entityName + "Service" + suffix;
        }
        if (temp.equals("mapper")) {
            fileName = entityName + "Mapper"  + suffix;
        }
        if (temp.equals("serviceImpl")) {
            fileName = entityName + "ServiceImpl" + suffix;
        }
        if (temp.equals("index")) {
            fileName = "vue3." + suffix;
        }
        //返回
        return fileName;
    }

    /**
     * 获取文件目录
     *
     * @param packageName
     * @param template
     * @return
     */
    private String getFileDir(String packageName, String template) {
        String templatePackageName = GenerateConfig.getBasePath() + packageName.replaceAll("\\.", "\\\\\\\\");
        List<String> list = Arrays.asList(template.split("/"));
        String temp = list.get(list.size() - 1).split("\\.")[0];
        if (temp.equals("serviceImpl")) {
            return templatePackageName + "\\\\" + "service\\\\impl";
        }
        //返回，取资源路径的倒数第二个作为包名
        return templatePackageName + "\\\\" + temp;
    }

    /**
     * 生成zip文件
     */
    private void genCode(HttpServletResponse response, byte[] data,String fileName) throws IOException{
        //新增  Content-Disposition 暴露到前端，否则无法显示
        response.addHeader("Access-Control-Expose-Headers", "Content-Disposition");
        fileName = URLEncoder.encode(fileName, Charsets.UTF_8.name());
        response.setHeader("Content-Disposition", "attachment; filename="+ fileName + ".zip");
        response.addHeader("Content-Length", "" + data.length);
        response.setContentType("application/octet-stream; charset=UTF-8");
        IOUtils.write(data, response.getOutputStream());
    }


    /**
     * 查询表列属性信息
     *
     * @param tableName 表名称
     * @return
     */
    @Override
    public List<TableColumnInfo> getTableColumnInfo(String tableName) {
        return baseMapper.getTableColumnFiledInfo(tableName);
    }
}
